using System;
using Xunit;
using 語法分析;
using 語義分析;

namespace ChineseCompiler.Tests
{
    /// <summary>
    /// 語義分析器測試類
    /// </summary>
    public class 語義分析測試
    {
        [Fact]
        public void 測試變量聲明語義分析()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            var 變量聲明 = new 變量聲明節點(1, 1)
            {
                變量類型 = "一數",
                變量名稱 = "甲",
                初始值 = new 字面量表達式節點(1, 5)
                {
                    類型 = 詞法分析.詞法單元類型.數字,
                    值 = 3
                }
            };
            程序.語句列表.Add(變量聲明);
            
            // Act & Assert - 不應拋出異常
            分析器.分析(程序);
        }
        
        [Fact]
        public void 測試重複變量聲明錯誤()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 變量聲明1 = new 變量聲明節點(1, 1)
            {
                變量類型 = "一數",
                變量名稱 = "甲"
            };
            
            var 變量聲明2 = new 變量聲明節點(2, 1)
            {
                變量類型 = "一言",
                變量名稱 = "甲" // 重複的變量名
            };
            
            程序.語句列表.Add(變量聲明1);
            程序.語句列表.Add(變量聲明2);
            
            // Act & Assert
            Assert.Throws<Exception>(() => 分析器.分析(程序));
        }
        
        [Fact]
        public void 測試未聲明變量使用錯誤()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 輸出語句 = new 輸出語句節點(1, 1)
            {
                輸出表達式 = new 標識符表達式節點(1, 3)
                {
                    名稱 = "未聲明變量"
                }
            };
            程序.語句列表.Add(輸出語句);
            
            // Act & Assert
            Assert.Throws<Exception>(() => 分析器.分析(程序));
        }
        
        [Fact]
        public void 測試賦值語句語義分析()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            // 先聲明變量
            var 變量聲明 = new 變量聲明節點(1, 1)
            {
                變量類型 = "一數",
                變量名稱 = "甲"
            };
            
            // 然後賦值
            var 賦值語句 = new 賦值語句節點(2, 1)
            {
                變量名稱 = "甲",
                賦值表達式 = new 字面量表達式節點(2, 5)
                {
                    類型 = 詞法分析.詞法單元類型.數字,
                    值 = 5
                }
            };
            
            程序.語句列表.Add(變量聲明);
            程序.語句列表.Add(賦值語句);
            
            // Act & Assert - 不應拋出異常
            分析器.分析(程序);
        }
        
        [Fact]
        public void 測試賦值未聲明變量錯誤()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 賦值語句 = new 賦值語句節點(1, 1)
            {
                變量名稱 = "未聲明變量",
                賦值表達式 = new 字面量表達式節點(1, 5)
                {
                    類型 = 詞法分析.詞法單元類型.數字,
                    值 = 5
                }
            };
            程序.語句列表.Add(賦值語句);
            
            // Act & Assert
            Assert.Throws<Exception>(() => 分析器.分析(程序));
        }
        
        [Fact]
        public void 測試條件語句語義分析()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 條件語句 = new 條件語句節點(1, 1)
            {
                條件表達式 = new 二元運算表達式節點(1, 3)
                {
                    左操作數 = new 字面量表達式節點(1, 3) { 類型 = 詞法分析.詞法單元類型.數字, 值 = 3 },
                    運算符 = "大於",
                    右操作數 = new 字面量表達式節點(1, 7) { 類型 = 詞法分析.詞法單元類型.數字, 值 = 2 }
                }
            };
            
            條件語句.真分支.Add(new 輸出語句節點(2, 1)
            {
                輸出表達式 = new 字面量表達式節點(2, 3)
                {
                    類型 = 詞法分析.詞法單元類型.字符串,
                    值 = "真"
                }
            });
            
            程序.語句列表.Add(條件語句);
            
            // Act & Assert - 不應拋出異常
            分析器.分析(程序);
        }
        
        [Fact]
        public void 測試函數定義語義分析()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 函數定義 = new 函數定義節點(1, 1)
            {
                函數名稱 = "測試函數"
            };
            
            函數定義.參數列表.Add(new 參數節點(1, 10)
            {
                參數類型 = "一數",
                參數名稱 = "參數1"
            });
            
            函數定義.函數體.Add(new 返回語句節點(2, 1)
            {
                返回值 = new 標識符表達式節點(2, 5)
                {
                    名稱 = "參數1"
                }
            });
            
            程序.語句列表.Add(函數定義);
            
            // Act & Assert - 不應拋出異常
            分析器.分析(程序);
        }
        
        [Fact]
        public void 測試重複函數定義錯誤()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 函數定義1 = new 函數定義節點(1, 1)
            {
                函數名稱 = "重複函數"
            };
            
            var 函數定義2 = new 函數定義節點(5, 1)
            {
                函數名稱 = "重複函數" // 重複的函數名
            };
            
            程序.語句列表.Add(函數定義1);
            程序.語句列表.Add(函數定義2);
            
            // Act & Assert
            Assert.Throws<Exception>(() => 分析器.分析(程序));
        }
        
        [Fact]
        public void 測試重複參數名錯誤()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 函數定義 = new 函數定義節點(1, 1)
            {
                函數名稱 = "測試函數"
            };
            
            函數定義.參數列表.Add(new 參數節點(1, 10)
            {
                參數類型 = "一數",
                參數名稱 = "重複參數"
            });
            
            函數定義.參數列表.Add(new 參數節點(1, 20)
            {
                參數類型 = "一言",
                參數名稱 = "重複參數" // 重複的參數名
            });
            
            程序.語句列表.Add(函數定義);
            
            // Act & Assert
            Assert.Throws<Exception>(() => 分析器.分析(程序));
        }
        
        [Fact]
        public void 測試循環語句語義分析()
        {
            // Arrange
            var 分析器 = new 語義分析器();
            var 程序 = new 程序節點();
            
            var 循環語句 = new 循環語句節點(1, 1);
            循環語句.循環體.Add(new 輸出語句節點(2, 1)
            {
                輸出表達式 = new 字面量表達式節點(2, 3)
                {
                    類型 = 詞法分析.詞法單元類型.字符串,
                    值 = "循環中"
                }
            });
            
            程序.語句列表.Add(循環語句);
            
            // Act & Assert - 不應拋出異常
            分析器.分析(程序);
        }
    }
} 